/*
 * Copyright (c) 2013, SRI International
 * All rights reserved.
 * Licensed under the The BSD 3-Clause License;
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 * 
 * http://opensource.org/licenses/BSD-3-Clause
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * 
 * Neither the name of the aic-web-praise nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.sri.ai.lpitools.executionengine.server;

import java.util.Random;
import java.util.concurrent.CountDownLatch;

import org.apache.log4j.Logger;
import org.apache.xmlrpc.XmlRpcException;

import com.sri.ai.lpitools.executionengine.server.implementations.ExecutionEngineServerWrapper;

public class ExecutionEngineThread extends Thread {

	private Logger logger = Logger.getLogger(getClass());
	
	private int job;
	private ExecutionEngineServerWrapper server;
	CountDownLatch latch;
	private String result;
	private String threadID;
	public ExecutionEngineThread(ExecutionEngineServerWrapper server, CountDownLatch latch, int job, String threadID){
		this.server = server;
		this.latch = latch;
		this.job = job;
		this.setThreadID(threadID);
	}

	public String getResult(){
		return result;
	}
	@Override
	public void run() {
        try {
            latch.await();         
        } catch (InterruptedException e) {
            logger.error(e.getMessage(), e);
        }
        switch (job) {
	        case 1:  runQueryByKey();
	                 break;
	        case 2:  checkModelByModel();
	                 break;
	        case 3:  getUniqueID();
	                 break;
	        default: runQueryByKey();
	                 break;
        }		
	}
	
	private void getUniqueID() {
		String answer = server.getUniqueID();
		if(answer != null){
			result ="true";
		} else {
			result = "false";
		}	
	}
	
	private void checkModelByModel() {
		String model = "'Gave Treasure To', 'An example of how hard it is to model things without aggregate factors.', sort(People, Unknown, { ann, bob }), sort(Treasure, Unknown, { gold, silver, diamonds }), randomVariable(gaveTreasureTo, 3, People, Treasure, People), randomVariable(owns, 2, People, Treasure, Boolean), randomVariable(rich, 1, People, Boolean), parfactors(union({{(on X in People, Y in People, Z in Treasure) [if gaveTreasureTo(X,Z,Y) then (if owns(Y,Z) then 1 else 0)  else 1]}}, {{(on X in People, Z in Treasure) [if owns(X,Z) then if rich(X) then 1 else 0 else 1] }}))";
		String expectedResult = "Model is valid";
		try {
			String answer = server.checkModelByModel(model);
			if(answer.contains(expectedResult)){
				result ="true";
			} else {
				result = "false";
			}
		} catch (XmlRpcException e) {
			logger.error(e.getMessage(), e);
		}		
	}
	
	private void runQueryByKey() {
	    int START = 1;
	    int END = 2;
	    Random random = new Random();
		//get the range, casting to long to avoid overflow problems
		long range = (long)END - (long)START + 1;
		// compute a fraction of the range, 0 <= frac < range
		long fraction = (long)(range * random.nextDouble());
		int randomNumber =  (int)(fraction + START); 
        switch (randomNumber) {
	        case 1:  runQueryByKeyCase1();
	                 break;
	        case 2:  runQueryByKeyCase2();
	                 break;
	        default: runQueryByKey();
	        		 break;
        }
	}

	private void runQueryByKeyCase1() {
		String model = "'Gave Treasure To', 'An example of how hard it is to model things without aggregate factors.', sort(People, Unknown, { ann, bob }), sort(Treasure, Unknown, { gold, silver, diamonds }), randomVariable(gaveTreasureTo, 3, People, Treasure, People), randomVariable(owns, 2, People, Treasure, Boolean), randomVariable(rich, 1, People, Boolean), parfactors(union({{(on X in People, Y in People, Z in Treasure) [if gaveTreasureTo(X,Z,Y) then (if owns(Y,Z) then 1 else 0)  else 1]}}, {{(on X in People, Z in Treasure) [if owns(X,Z) then if rich(X) then 1 else 0 else 1] }}))";
		String user ="admin";
		String evidenceKey ="";
		String query = "belief([rich(bob)])";
		String queryOptionsJSON = "{\"knownDomainSizes\":true,\"traceOn\":false,\"justificationsOn\":false}";
		String clientID = "0";
		String url = "128.57.245.33:9000";
		
		String expectedResult = "\"result\": \"'atomic rule'(rich(bob), 1)\"";
		try {
			String answer = server.runQueryByKey(user, model, evidenceKey, query, clientID, queryOptionsJSON, url);
			if(answer.contains(expectedResult)){
				result ="true";
			} else {
				result = "false";
			}
		} catch (XmlRpcException e) {
			logger.error(e.getMessage(), e);
		}
	}
	
	private void runQueryByKeyCase2() {
		String model = "'Gave Treasure To', 'An example of how hard it is to model things without aggregate factors.', sort(People, Unknown, { ann, bob }), sort(Treasure, Unknown, { gold, silver, diamonds }), randomVariable(gaveTreasureTo, 3, People, Treasure, People), randomVariable(owns, 2, People, Treasure, Boolean), randomVariable(rich, 1, People, Boolean), parfactors(union({{(on X in People, Y in People, Z in Treasure) [if gaveTreasureTo(X,Z,Y) then (if owns(Y,Z) then 1 else 0)  else 1]}}, {{(on X in People, Z in Treasure) [if owns(X,Z) then if rich(X) then 1 else 0 else 1] }}))";
		String user ="admin";
		String evidenceKey ="";
		String query = "belief([rich(ann)])";
		String queryOptionsJSON = "{\"knownDomainSizes\":true,\"traceOn\":false,\"justificationsOn\":false}";
		String clientID = "0";
		String url = "128.57.245.33:9000";
		
		String expectedResult = "\"result\": \"'atomic rule'(rich(ann), 1)\"";
		try {
			String answer = server.runQueryByKey(user, model, evidenceKey, query, clientID, queryOptionsJSON, url);
			if(answer.contains(expectedResult)){
				result ="true";
			} else {
				result = "false";
			}
		} catch (XmlRpcException e) {
			logger.error(e.getMessage(), e);
		}
	}
	
	/**
	 * @return the threadID
	 */
	public String getThreadID() {
		return threadID;
	}

	/**
	 * @param threadID the threadID to set
	 */
	public void setThreadID(String threadID) {
		this.threadID = threadID;
	}
}
